This dataset contains nuclei of CD45+CD11b+F4/80+CD115+ FACS sorted monocyte enriched cells from mouse E14 fetal liver and adult bone marrow. The input files can be downloaded from GSE292830.

The mouse brain Signac vignette was followed for the data processing: https://stuartlab.org/signac/articles/mouse_brain_vignette

suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(Signac))
suppressPackageStartupMessages(library(Seurat, lib.loc="/home/daliya/Apps/Seurat.v4"))
suppressPackageStartupMessages(library(ggplot2))
suppressPackageStartupMessages(library(cowplot))
suppressPackageStartupMessages(library(clustree))
suppressPackageStartupMessages(library(plotly))
suppressPackageStartupMessages(library(presto))
suppressPackageStartupMessages(library(scater))
suppressPackageStartupMessages(library(EnsDb.Mmusculus.v79))

Set the working directory

knitr::opts_knit$set(root.dir = "~/Documents/Kia/Jonathan/JBA1-2_ATAC/results/")
path<-"/media/daliya/SSD2/0-0-Exp4885-JBA/"
path.output<-"~/Documents/Kia/Jonathan/JBA1-2_ATAC/results/"
sample.names<-c("JBA1","JBA2")
sample.description=c("BM","Fetal liver")
mm10.blacklist = rtracklayer:: import(gzfile(  "/media/daliya/SSD2/ATAC_blacklist/mm10.blacklist.bed.gz")) ### Blacklist downloaded from https://github.com/Boyle-Lab/Blacklist?tab=readme-ov-file

Pre-processing

When pre-processing chromatin data, Signac uses information from two related input files, both of which can be created using CellRanger:

We start by creating a Seurat object using the peak/cell matrix and cell metadata generated by cellranger-atac, and store the path to the fragment file on disk in the Seurat object:

seur=list()
for (i in 1:length(sample.names)){
chrassay<- CreateChromatinAssay(
  counts =  Read10X_h5(paste0(path,sample.names[i],"/filtered_peak_bc_matrix.h5")),
  sep = c(":", "-"),
  genome = "mm10",
  fragments = paste0(path,sample.names[i],'/fragments.tsv.gz'),
  min.cells = 1,
  verbose =F
)
metadata <- read.csv(paste0(path,sample.names[i],"/singlecell.csv"),  header = TRUE,row.names = 1)
seur[[i]] <- CreateSeuratObject(
  counts = chrassay,
  assay = 'peaks',
  project = 'ATAC',
  meta.data = metadata
)
seur[[i]]$sample=sample.names[i]
seur[[i]]$origin=sample.description[i]
}
rm(chrassay)
rm(metadata)

We can also add gene annotations to the brain object for the mouse genome. This will allow downstream functions to pull the gene annotation information directly from the object.

annotations <- GetGRangesFromEnsDb(ensdb = EnsDb.Mmusculus.v79, verbose=F)

# change to UCSC style since the data was mapped to hg19
seqlevels(annotations) <- paste0('chr', seqlevels(annotations))
genome(annotations) <- "mm10"

# add the gene information to the object

for (i in 1:length(sample.names)){
Annotation(seur[[i]]) <- annotations
}

Computing QC Metrics

for (i in 1:length(sample.names)){
seur[[i]] <- NucleosomeSignal(object = seur[[i]], verbose=F)
}
for (i in 1:length(sample.names)){
seur[[i]]$nucleosome_group <- ifelse(seur[[i]]$nucleosome_signal > 4, 'NS > 4', 'NS < 4')
print(FragmentHistogram(object = seur[[i]], group.by = 'nucleosome_group', region = 'chr1-1-10000000'))
}

for (i in 1:length(sample.names)){
seur[[i]] <- TSSEnrichment(seur[[i]], verbose=F)
}
for (i in 1:length(sample.names)){
seur[[i]] $pct_reads_in_peaks <- seur[[i]] $peak_region_fragments / seur[[i]] $passed_filters * 100
}
seur[[i]]$blacklist_ratio <- FractionCountsInRegion(
  object = seur[[i]], 
  assay = 'peaks',
  regions = mm10.blacklist
)

Fidning outlier nuclei

The exact QC thresholds for removing outliers need to be adjusted according to the dataset. Here, we use the isOutlier() function from the scuttle package. It defines an observation as an outlier if it is more than a specified number of median absolute deviations (MADs, default 3) from the median in the specified direction.

  j= 'nucleosome_signal'
for (i in 1:length(sample.names)){
  outliers= scuttle::isOutlier(seur[[i]]@meta.data[,j], nmads=6, type="higher", log=TRUE)
  attr(outliers, "thresholds") <- NULL
 seur[[i]]@meta.data[paste0(j,".outlier.higher")] <- outliers
  cat(sample.names[i],j,"outliers higher :",sum(seur[[i]]@meta.data[paste0(j,".outlier.higher")]),"\n")
}
JBA1 nucleosome_signal outliers higher : 218 
JBA2 nucleosome_signal outliers higher : 223 
for ( j in c('pct_reads_in_peaks','TSS.enrichment')){
 for (i in 1:length(sample.names)){
    outliers= scater::isOutlier(seur[[i]]@meta.data[,j], nmads=6, type="lower", log=TRUE)
  attr(outliers, "thresholds") <- NULL
 seur[[i]]@meta.data[paste0(j,".outlier.lower")] <- outliers
  cat(sample.names[i],j,"outliers lower :",sum(seur[[i]]@meta.data[paste0(j,".outlier.lower")]),"\n")
  }
}
JBA1 pct_reads_in_peaks outliers lower : 326 
JBA2 pct_reads_in_peaks outliers lower : 214 
JBA1 TSS.enrichment outliers lower : 19 
JBA2 TSS.enrichment outliers lower : 55 
j='peak_region_fragments'
for (i in 1:length(sample.names)){
    outliers= scater::isOutlier(seur[[i]]@meta.data[,j], nmads=2, type="lower", log=TRUE)
  attr(outliers, "thresholds") <- NULL
 seur[[i]]@meta.data[paste0(j,".outlier.lower")] <- outliers
  cat(sample.names[i],j,"outliers lower :",sum(seur[[i]]@meta.data[paste0(j,".outlier.lower")]),"\n")
}
JBA1 peak_region_fragments outliers lower : 859 
JBA2 peak_region_fragments outliers lower : 1412 
j="blacklist_ratio"
for (i in 1:length(sample.names)){
  outliers= scater::isOutlier(seur[[i]]@meta.data[,j], nmads=3, type="higher", log=TRUE)
  attr(outliers, "thresholds") <- NULL
 seur[[i]]@meta.data[paste0(j,".outlier.higher")] <- outliers
  cat(sample.names[i],j,"outliers higher :",sum(seur[[i]]@meta.data[paste0(j,".outlier.higher")]),"\n")
}
JBA1 blacklist_ratio outliers higher : 35 
JBA2 blacklist_ratio outliers higher : 315 
for ( j in c('peak_region_fragments','pct_reads_in_peaks', 'nucleosome_signal',
               'TSS.enrichment', "blacklist_ratio")){
  for (i in 1:length(sample.names)){
     hist(seur[[i]]@meta.data[,j],
        breaks = 100,xlab=j,
        main=paste0(j,": ",sample.names[i]))
    if (paste0(j,".outlier.higher") %in% colnames(seur[[i]]@meta.data) ){
      if(sum(seur[[i]]@meta.data[paste0(j,".outlier.higher")]) !=0 )
       abline(v = min(seur[[i]]@meta.data[,j][unlist(seur[[i]]@meta.data[paste0(j,".outlier.higher")])]), col = "red")
    }
   if (paste0(j,".outlier.lower") %in% colnames(seur[[i]]@meta.data) ){
    if(sum(seur[[i]]@meta.data[paste0(j,".outlier.lower")]) !=0 )
   abline(v = max(seur[[i]]@meta.data[,j][unlist(seur[[i]]@meta.data[paste0(j,".outlier.lower")])]), col = "red")
   }
  }
}

We remove cells that are outliers for these QC metrics.

for (i in 1:length(sample.names)){
seur[[i]]  <- seur[[i]] [,!(seur[[i]]$peak_region_fragments.outlier.lower | 
    seur[[i]]$pct_reads_in_peaks.outlier.lower | 
    seur[[i]]$nucleosome_signal.outlier.higher  | 
    seur[[i]]$TSS.enrichment.outlier.lower |
   seur[[i]]$blacklist_ratio.outlier.higher   )]

print(sample.names[i])
print(seur[[i]] )
}
[1] "JBA1"
An object of class Seurat 
178086 features across 18322 samples within 1 assay 
Active assay: peaks (178086 features, 0 variable features)
 2 layers present: counts, data
[1] "JBA2"
An object of class Seurat 
180414 features across 17338 samples within 1 assay 
Active assay: peaks (180414 features, 0 variable features)
 2 layers present: counts, data
seur<-merge(seur[[1]],seur[2:length(seur)])

Normalization and linear dimensional reduction

Normalization: Signac performs term frequency-inverse document frequency (TF-IDF) normalization. This is a two-step normalization procedure, that both normalizes across cells to correct for differences in cellular sequencing depth, and across peaks to give higher values to more rare peaks.

Feature selection: The low dynamic range of scATAC-seq data makes it challenging to perform variable feature selection, as we do for scRNA-seq. Instead, we can choose to use only the top n% of features (peaks) for dimensional reduction, or remove features present in less than n cells with the FindTopFeatures() function. Here we will use all features, though we have seen very similar results when using only a subset of features (try setting min.cutoff to ‘q75’ to use the top 25% all peaks), with faster runtimes. Features used for dimensional reduction are automatically set as VariableFeatures() for the Seurat object by this function.

Dimension reduction: We next run singular value decomposition (SVD) on the TD-IDF matrix, using the features (peaks) selected above. This returns a reduced dimension representation of the object (for users who are more familiar with scRNA-seq, you can think of this as analogous to the output of PCA).

The combined steps of TF-IDF followed by SVD are known as latent semantic indexing (LSI), and were first introduced for the analysis of scATAC-seq data by Cusanovich et al. 2015.

seur <- RunTFIDF(seur,verbose =F)
seur <- FindTopFeatures(seur, min.cutoff = 'q0',verbose =F)
seur <- RunSVD(object = seur,verbose =F)

The first LSI component often captures sequencing depth (technical variation) rather than biological variation. If this is the case, the component should be removed from downstream analysis. We can assess the correlation between each LSI component and sequencing depth using the DepthCor() function:

DepthCor(seur)

ElbowPlot(object = seur,ndims =50, reduction = "lsi")

Non-linear dimension reduction and clustering

Now that the cells are embedded in a low-dimensional space we can use methods commonly applied for the analysis of scRNA-seq data to perform graph-based clustering and non-linear dimension reduction for visualization. The functions RunUMAP(), FindNeighbors(), and FindClusters() all come from the Seurat package.

### LSI components selection for downstream analysis
dims.use<-30
seur <- RunUMAP(object = seur, reduction = 'lsi', dims = 2:dims.use,verbose =F)
seur <- FindNeighbors(seur,  reduction = 'lsi',dims = 2:dims.use, verbose=F, graph.name=paste0("ATAC_snn_LSI",dims.use))
for ( i in seq(0,2, 0.25))
  seur <- FindClusters(seur, resolution = i, algorithm = 3, graph.name=paste0("ATAC_snn_LSI",dims.use), verbose=F) 
clustree::clustree(seur, prefix = paste0("ATAC_snn_LSI",dims.use,"_res."))+
  ggtitle(paste("LSI =",dims.use))

plot<-list()
for ( res in c(0.25,0.5,0.75,1))
  plot[[as.character(res)]]<-DimPlot(seur,label=T,repel=T, group.by = paste0("ATAC_snn_LSI",dims.use,"_res.",res))+
                                      ggtitle(paste("LSI =",dims.use,"res=",res))
plot_grid(plotlist=plot)

DimPlot(seur,repel =T,label=T, group.by = "origin") 

Create a gene activity matrix

We can try to quantify the activity of each gene in the genome by assessing the chromatin accessibility associated with the gene, and create a new gene activity assay derived from the scATAC-seq data. Here we will use a simple approach of summing the fragments intersecting the gene body and promoter region

To create a gene activity matrix, we extract gene coordinates and extend them to include the 2 kb upstream region (as promoter accessibility is often correlated with gene expression). We then count the number of fragments for each cell that map to each of these regions, using the using the FeatureMatrix() function. These steps are automatically performed by the GeneActivity() function:

gene.activities <- GeneActivity(seur,verbose =F)

seur[['RNA']] <- CreateAssayObject(counts = gene.activities)
 # add the gene activity matrix to the Seurat object as a new assay and normalize it                               
seur <- NormalizeData(
  object = seur,
  assay = 'RNA',
  normalization.method = 'LogNormalize',
  scale.factor = median(seur$nCount_RNA),
  verbose =F
)
seur <- ScaleData(seur, assay = 'RNA',verbose =F)
DefaultAssay(seur) <- 'RNA'
res=0.5
Idents(seur)=  paste0("ATAC_snn_LSI",dims.use,"_res.",res)
Idents(seur)=  factor(Idents(seur),levels = 0:(length(unique(Idents(seur)))-1))
DEgenes=list()
for (i in levels(Idents(seur))){
 DEgenes[[i]]<-FindMarkers(seur, ident.1 = i,min.cells.group=2,pseudocount.use = 0.01, max.cells.per.ident = 1000)
 DEgenes[[i]]$cluster=i
 DEgenes[[i]]$pseudocount=0.01
 DEgenes[[i]]$max.cells.per.ident=1000
 DEgenes[[i]]$gene=row.names(DEgenes[[i]])
}
features.use=unlist(lapply(DEgenes, function(x) { head(x[x$avg_log2FC>0,]$gene,7)}))
length(features.use)
[1] 77
names(features.use)=NULL
features.use=features.use[!duplicated(features.use)]
DimPlot(seur,repel =T,label=T) 

DotPlot(seur, features = features.use)+RotatedAxis()+NoLegend()

Idents(seur) <- plyr::mapvalues(x = Idents(seur), from = 0:10, 
    to =c("FL 2", "BM 3","BM 1","FL 1","BM 4","BM 2",
          "FL 3", "FL 5", "Erythroid","FL 4","Lymphoid"))
new.order=c (  "FL 1", "FL 2" , "FL 3" ,  "FL 4","FL 5" ,  "BM 1"  , "BM 2", "BM 3"   ,  "BM 4" ,  "Erythroid" , "Lymphoid"       )
new.order[!new.order %in% levels( Idents(seur))]
character(0)
levels( Idents(seur))[!levels( Idents(seur)) %in%new.order]
character(0)
Idents(seur)=factor( Idents(seur), levels=new.order)
seur$annot=Idents(seur)
DimPlot(seur,repel =T,label=T) 

DotPlot(seur, features=c("Ptprc","Flt3","Tshz2","Igf2bp3" , "Gli2","Cd34","Prtn3","Kit","Mpo","Lpo","Elane" ,"Sorcs2","Pax5","Ror1","Tmtc1","C1qa","C1qb","Ms4a7","Apoe","Fcrls","Cdk8","Lars2","Plec","S100a10","F13a1","Ly6c2","Crip1" ,"Ccr2" ,"Lyz2", "Tgfbi","Mrc1","Ust","H2-Aa","Cd74","Atf3" ,"Klf13"   , "Grk5"  ,"Dock5"  , "Cyp2ab1" ,  "Ace","Ear2","Ank1","Gypa","Aqp1","Car2","Hbb-bt" ,"Ccr7","Cd79a","Ms4a1","Ebf1","Dntt","Mki67","Stmn1","nucleosome_signal","nucleosome_percentile","TSS.enrichment","TSS.percentile","pct_reads_in_peaks","blacklist_ratio"))+RotatedAxis()+theme(axis.title = element_blank())

meta.data<-seur@meta.data %>%
    dplyr::group_by(annot,origin) %>%
    dplyr::summarise(count=dplyr::n())%>% suppressMessages()%>%
    dplyr::group_by(annot) %>%
    dplyr::mutate(perc.per.group = (count / sum(count))*100)  
ggplot(meta.data, aes(x=annot, y=perc.per.group, fill=origin))+
    geom_bar(stat = "identity")+
    theme_classic()+
    theme(axis.title.x=element_blank(),
          axis.text = element_text(size=12),
          axis.title.y = element_text(size=12),
          legend.text=element_text(size=12),
          legend.title=element_text(size=12))+
    ylab("% cells per cluster")+RotatedAxis()

saveRDS(seur,paste0("Integrated.BM.and.FL_monocytes_snATACseq_JBA1-2.seurat.rds"))
sessionInfo()
R version 4.4.3 (2025-02-28)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 24.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.12.0 
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=de_BE.UTF-8        LC_COLLATE=en_US.UTF-8     LC_MONETARY=de_BE.UTF-8   
 [6] LC_MESSAGES=en_US.UTF-8    LC_PAPER=de_BE.UTF-8       LC_NAME=C                  LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=de_BE.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Brussels
tzcode source: system (glibc)

attached base packages:
[1] stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] EnsDb.Mmusculus.v79_2.99.0  ensembldb_2.30.0            AnnotationFilter_1.30.0     GenomicFeatures_1.58.0     
 [5] AnnotationDbi_1.68.0        scater_1.34.0               scuttle_1.16.0              SingleCellExperiment_1.28.1
 [9] SummarizedExperiment_1.36.0 Biobase_2.66.0              GenomicRanges_1.58.0        GenomeInfoDb_1.42.1        
[13] IRanges_2.40.0              S4Vectors_0.44.0            BiocGenerics_0.52.0         MatrixGenerics_1.18.0      
[17] matrixStats_1.4.1           presto_1.0.0                data.table_1.16.2           Rcpp_1.0.13-1              
[21] plotly_4.10.4               clustree_0.5.1              ggraph_2.2.1                cowplot_1.1.3              
[25] ggplot2_3.5.1               SeuratObject_5.0.2          Seurat_4.3.0                Signac_1.14.0              
[29] dplyr_1.1.4                

loaded via a namespace (and not attached):
  [1] RcppAnnoy_0.0.22         splines_4.4.3            later_1.4.1              BiocIO_1.16.0            bitops_1.0-9            
  [6] tibble_3.2.1             polyclip_1.10-7          XML_3.99-0.17            lifecycle_1.0.4          globals_0.16.3          
 [11] lattice_0.22-5           MASS_7.3-65              backports_1.5.0          magrittr_2.0.3           sass_0.4.9              
 [16] rmarkdown_2.29           jquerylib_0.1.4          yaml_2.3.10              httpuv_1.6.15            sctransform_0.4.1       
 [21] spam_2.11-0              sp_2.1-4                 spatstat.sparse_3.1-0    reticulate_1.40.0        DBI_1.2.3               
 [26] pbapply_1.7-2            RColorBrewer_1.1-3       abind_1.4-8              zlibbioc_1.52.0          Rtsne_0.17              
 [31] purrr_1.0.2              RCurl_1.98-1.16          tweenr_2.0.3             GenomeInfoDbData_1.2.13  ggrepel_0.9.6           
 [36] irlba_2.3.5.1            listenv_0.9.1            spatstat.utils_3.1-3     goftest_1.2-3            spatstat.random_3.3-3   
 [41] fitdistrplus_1.2-1       parallelly_1.40.0        leiden_0.4.3.1           codetools_0.2-20         DelayedArray_0.32.0     
 [46] RcppRoll_0.3.1           ggforce_0.4.2            tidyselect_1.2.1         UCSC.utils_1.2.0         farver_2.1.2            
 [51] ScaledMatrix_1.14.0      viridis_0.6.5            spatstat.explore_3.4-2   GenomicAlignments_1.42.0 jsonlite_1.8.9          
 [56] BiocNeighbors_2.0.1      tidygraph_1.3.1          progressr_0.15.1         ggridges_0.5.6           survival_3.8-3          
 [61] tools_4.4.3              ica_1.0-3                glue_1.8.0               gridExtra_2.3            SparseArray_1.6.0       
 [66] xfun_0.49                withr_3.0.2              fastmap_1.2.0            fansi_1.0.6              digest_0.6.37           
 [71] rsvd_1.0.5               R6_2.5.1                 mime_0.12                colorspace_2.1-1         scattermore_1.2         
 [76] tensor_1.5               RSQLite_2.3.8            spatstat.data_3.1-6      utf8_1.2.4               tidyr_1.3.1             
 [81] generics_0.1.3           rtracklayer_1.66.0       graphlayouts_1.2.1       httr_1.4.7               htmlwidgets_1.6.4       
 [86] S4Arrays_1.6.0           uwot_0.2.2               pkgconfig_2.0.3          gtable_0.3.6             blob_1.2.4              
 [91] lmtest_0.9-40            XVector_0.46.0           htmltools_0.5.8.1        dotCall64_1.2            ProtGenerics_1.38.0     
 [96] scales_1.3.0             png_0.1-8                spatstat.univar_3.1-2    knitr_1.49               rstudioapi_0.17.1       
[101] rjson_0.2.23             reshape2_1.4.4           checkmate_2.3.2          curl_6.0.1               nlme_3.1-167            
[106] zoo_1.8-12               cachem_1.1.0             stringr_1.5.1            KernSmooth_2.23-26       vipor_0.4.7             
[111] parallel_4.4.3           miniUI_0.1.1.1           restfulr_0.0.15          pillar_1.9.0             grid_4.4.3              
[116] vctrs_0.6.5              RANN_2.6.2               promises_1.3.2           BiocSingular_1.22.0      beachmat_2.22.0         
[121] xtable_1.8-4             cluster_2.1.8.1          beeswarm_0.4.0           evaluate_1.0.1           cli_3.6.3               
[126] compiler_4.4.3           Rsamtools_2.22.0         rlang_1.1.4              crayon_1.5.3             future.apply_1.11.3     
[131] labeling_0.4.3           plyr_1.8.9               ggbeeswarm_0.7.2         stringi_1.8.4            viridisLite_0.4.2       
[136] deldir_2.0-4             BiocParallel_1.40.0      munsell_0.5.1            Biostrings_2.74.0        lazyeval_0.2.2          
[141] spatstat.geom_3.3-6      Matrix_1.7-1             patchwork_1.3.0          bit64_4.5.2              future_1.34.0           
[146] KEGGREST_1.46.0          shiny_1.9.1              ROCR_1.0-11              igraph_2.1.2             memoise_2.0.1           
[151] bslib_0.8.0              fastmatch_1.1-4          bit_4.5.0.1             
LS0tCnRpdGxlOiAic25BVEFDc2VxIGRhdGEgcHJvY2Vzc2luZyIKb3V0cHV0OiBodG1sX25vdGVib29rCmRhdGU6ICdDcmVhdGVkIG9uOiBgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVCICVkLCAlWSIpYCcKLS0tCgpUaGlzIGRhdGFzZXQgY29udGFpbnMgbnVjbGVpIG9mIENENDUrQ0QxMWIrRjQvODArQ0QxMTUrIEZBQ1Mgc29ydGVkIG1vbm9jeXRlIGVucmljaGVkIGNlbGxzIGZyb20gbW91c2UgRTE0IGZldGFsIGxpdmVyIGFuZCBhZHVsdCBib25lIG1hcnJvdy4gVGhlIGlucHV0IGZpbGVzIGNhbiBiZSBkb3dubG9hZGVkIGZyb20gR1NFMjkyODMwLgoKVGhlIG1vdXNlIGJyYWluIFNpZ25hYyB2aWduZXR0ZSB3YXMgZm9sbG93ZWQgZm9yIHRoZSBkYXRhIHByb2Nlc3Npbmc6IGh0dHBzOi8vc3R1YXJ0bGFiLm9yZy9zaWduYWMvYXJ0aWNsZXMvbW91c2VfYnJhaW5fdmlnbmV0dGUgCgpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZHBseXIpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShTaWduYWMpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShTZXVyYXQsIGxpYi5sb2M9Ii9ob21lL2RhbGl5YS9BcHBzL1NldXJhdC52NCIpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShnZ3Bsb3QyKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoY293cGxvdCkpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KGNsdXN0cmVlKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkocGxvdGx5KSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkocHJlc3RvKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoc2NhdGVyKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoRW5zRGIuTW11c2N1bHVzLnY3OSkpCmBgYAoKU2V0IHRoZSB3b3JraW5nIGRpcmVjdG9yeQpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAifi9Eb2N1bWVudHMvS2lhL0pvbmF0aGFuL0pCQTEtMl9BVEFDL3Jlc3VsdHMvIikKYGBgCgpgYGB7cn0Kc2FtcGxlLm5hbWVzPC1jKCJKQkExIiwiSkJBMiIpCnNhbXBsZS5kZXNjcmlwdGlvbj1jKCJCTSIsIkZldGFsIGxpdmVyIikKbW0xMC5ibGFja2xpc3QgPSBydHJhY2tsYXllcjo6IGltcG9ydChnemZpbGUoICAibW0xMC5ibGFja2xpc3QuYmVkLmd6IikpICMjIyBCbGFja2xpc3QgZG93bmxvYWRlZCBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9Cb3lsZS1MYWIvQmxhY2tsaXN0P3RhYj1yZWFkbWUtb3YtZmlsZQpgYGAKCgojIyMgUHJlLXByb2Nlc3NpbmcgCldoZW4gcHJlLXByb2Nlc3NpbmcgY2hyb21hdGluIGRhdGEsIFNpZ25hYyB1c2VzIGluZm9ybWF0aW9uIGZyb20gdHdvIHJlbGF0ZWQgaW5wdXQgZmlsZXMsIGJvdGggb2Ygd2hpY2ggY2FuIGJlIGNyZWF0ZWQgdXNpbmcgQ2VsbFJhbmdlcjoKCiAgIC0gUGVhay9DZWxsIG1hdHJpeC4gVGhpcyBpcyBhbmFsb2dvdXMgdG8gdGhlIGdlbmUgZXhwcmVzc2lvbiBjb3VudCBtYXRyaXggdXNlZCB0byBhbmFseXplIHNpbmdsZS1jZWxsIFJOQS1zZXEuIEhvd2V2ZXIsIGluc3RlYWQgb2YgZ2VuZXMsIGVhY2ggcm93IG9mIHRoZSBtYXRyaXggcmVwcmVzZW50cyBhIHJlZ2lvbiBvZiB0aGUgZ2Vub21lIChhIHBlYWspLCB0aGF0IGlzIHByZWRpY3RlZCB0byByZXByZXNlbnQgYSByZWdpb24gb2Ygb3BlbiBjaHJvbWF0aW4uIEVhY2ggdmFsdWUgaW4gdGhlIG1hdHJpeCByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgVG41IGludGVncmF0aW9uIHNpdGVzIGZvciBlYWNoIHNpbmdsZSBiYXJjb2RlIChpLmUuIGEgY2VsbCkgdGhhdCBtYXAgd2l0aGluIGVhY2ggcGVhay4gWW91IGNhbiBmaW5kIG1vcmUgZGV0YWlsIG9uIHRoZSAxMFggV2Vic2l0ZS4KICAgLSBGcmFnbWVudCBmaWxlLiBUaGlzIHJlcHJlc2VudHMgYSBmdWxsIGxpc3Qgb2YgYWxsIHVuaXF1ZSBmcmFnbWVudHMgYWNyb3NzIGFsbCBzaW5nbGUgY2VsbHMuIEl0IGlzIGEgc3Vic3RhbnRpYWxseSBsYXJnZXIgZmlsZSwgaXMgc2xvd2VyIHRvIHdvcmsgd2l0aCwgYW5kIGlzIHN0b3JlZCBvbi1kaXNrIChpbnN0ZWFkIG9mIGluIG1lbW9yeSkuIEhvd2V2ZXIsIHRoZSBhZHZhbnRhZ2Ugb2YgcmV0YWluaW5nIHRoaXMgZmlsZSBpcyB0aGF0IGl0IGNvbnRhaW5zIGFsbCBmcmFnbWVudHMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggc2luZ2xlIGNlbGwsIGFzIG9wcG9zZWQgdG8gb25seSBmcmFnbWVudHMgdGhhdCBtYXAgdG8gcGVha3MuIE1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGZyYWdtZW50IGZpbGUgY2FuIGJlIGZvdW5kIG9uIHRoZSAxMHggR2Vub21pY3Mgd2Vic2l0ZSBvciBvbiB0aGUgc2ludG8gd2Vic2l0ZS4KCldlIHN0YXJ0IGJ5IGNyZWF0aW5nIGEgU2V1cmF0IG9iamVjdCB1c2luZyB0aGUgcGVhay9jZWxsIG1hdHJpeCBhbmQgY2VsbCBtZXRhZGF0YSBnZW5lcmF0ZWQgYnkgY2VsbHJhbmdlci1hdGFjLCBhbmQgc3RvcmUgdGhlIHBhdGggdG8gdGhlIGZyYWdtZW50IGZpbGUgb24gZGlzayBpbiB0aGUgU2V1cmF0IG9iamVjdDoKCmBgYHtyfQpzZXVyPWxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgoc2FtcGxlLm5hbWVzKSl7CmNocmFzc2F5PC0gQ3JlYXRlQ2hyb21hdGluQXNzYXkoCiAgY291bnRzID0gIFJlYWQxMFhfaDUocGFzdGUwKHNhbXBsZS5uYW1lc1tpXSwiL2ZpbHRlcmVkX3BlYWtfYmNfbWF0cml4Lmg1IikpLAogIHNlcCA9IGMoIjoiLCAiLSIpLAogIGdlbm9tZSA9ICJtbTEwIiwKICBmcmFnbWVudHMgPSBwYXN0ZTAoc2FtcGxlLm5hbWVzW2ldLCcvZnJhZ21lbnRzLnRzdi5neicpLAogIG1pbi5jZWxscyA9IDEsCiAgdmVyYm9zZSA9RgopCm1ldGFkYXRhIDwtIHJlYWQuY3N2KHBhc3RlMChzYW1wbGUubmFtZXNbaV0sIi9zaW5nbGVjZWxsLmNzdiIpLCAgaGVhZGVyID0gVFJVRSxyb3cubmFtZXMgPSAxKQpzZXVyW1tpXV0gPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KAogIGNvdW50cyA9IGNocmFzc2F5LAogIGFzc2F5ID0gJ3BlYWtzJywKICBwcm9qZWN0ID0gJ0FUQUMnLAogIG1ldGEuZGF0YSA9IG1ldGFkYXRhCikKc2V1cltbaV1dJHNhbXBsZT1zYW1wbGUubmFtZXNbaV0Kc2V1cltbaV1dJG9yaWdpbj1zYW1wbGUuZGVzY3JpcHRpb25baV0KfQpybShjaHJhc3NheSkKcm0obWV0YWRhdGEpCmBgYAoKV2UgY2FuIGFsc28gYWRkIGdlbmUgYW5ub3RhdGlvbnMgdG8gdGhlIGJyYWluIG9iamVjdCBmb3IgdGhlIG1vdXNlIGdlbm9tZS4gVGhpcyB3aWxsIGFsbG93IGRvd25zdHJlYW0gZnVuY3Rpb25zIHRvIHB1bGwgdGhlIGdlbmUgYW5ub3RhdGlvbiBpbmZvcm1hdGlvbiBkaXJlY3RseSBmcm9tIHRoZSBvYmplY3QuCgoKYGBge3J9CmFubm90YXRpb25zIDwtIEdldEdSYW5nZXNGcm9tRW5zRGIoZW5zZGIgPSBFbnNEYi5NbXVzY3VsdXMudjc5LCB2ZXJib3NlPUYpCgojIGNoYW5nZSB0byBVQ1NDIHN0eWxlIHNpbmNlIHRoZSBkYXRhIHdhcyBtYXBwZWQgdG8gaGcxOQpzZXFsZXZlbHMoYW5ub3RhdGlvbnMpIDwtIHBhc3RlMCgnY2hyJywgc2VxbGV2ZWxzKGFubm90YXRpb25zKSkKZ2Vub21lKGFubm90YXRpb25zKSA8LSAibW0xMCIKCiMgYWRkIHRoZSBnZW5lIGluZm9ybWF0aW9uIHRvIHRoZSBvYmplY3QKCmZvciAoaSBpbiAxOmxlbmd0aChzYW1wbGUubmFtZXMpKXsKQW5ub3RhdGlvbihzZXVyW1tpXV0pIDwtIGFubm90YXRpb25zCn0KCmBgYAoKCiMjIyBDb21wdXRpbmcgUUMgTWV0cmljcwpgYGB7cn0KZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZS5uYW1lcykpewpzZXVyW1tpXV0gPC0gTnVjbGVvc29tZVNpZ25hbChvYmplY3QgPSBzZXVyW1tpXV0sIHZlcmJvc2U9RikKfQpgYGAKCi0gTnVjbGVvc29tZSBiYW5kaW5nIHBhdHRlcm4KV2UgY2FuIGxvb2sgYXQgdGhlIGZyYWdtZW50IGxlbmd0aCBwZXJpb2RpY2l0eSBmb3IgYWxsIHRoZSBjZWxscywgYW5kIGdyb3VwIGJ5IGNlbGxzIHdpdGggaGlnaCBvciBsb3cgbnVjbGVvc29tYWwgc2lnbmFsIHN0cmVuZ3RoLiBDZWxscyB3aGljaCBhcmUgb3V0bGllcnMgZm9yIHRoZSBtb25vbnVjbGVvc29tYWwvIG51Y2xlb3NvbWUtZnJlZSByYXRpbyBoYXZlIGRpZmZlcmVudCBiYW5kaW5nIHBhdHRlcm5zLiBUaGUgcmVtYWluaW5nIGNlbGxzIGV4aGliaXQgYSBwYXR0ZXJuIHRoYXQgaXMgdHlwaWNhbCBmb3IgYSBzdWNjZXNzZnVsIEFUQUMtc2VxIGV4cGVyaW1lbnQuCgpgYGB7ciAsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxMn0KZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZS5uYW1lcykpewpzZXVyW1tpXV0kbnVjbGVvc29tZV9ncm91cCA8LSBpZmVsc2Uoc2V1cltbaV1dJG51Y2xlb3NvbWVfc2lnbmFsID4gNCwgJ05TID4gNCcsICdOUyA8IDQnKQpwcmludChGcmFnbWVudEhpc3RvZ3JhbShvYmplY3QgPSBzZXVyW1tpXV0sIGdyb3VwLmJ5ID0gJ251Y2xlb3NvbWVfZ3JvdXAnLCByZWdpb24gPSAnY2hyMS0xLTEwMDAwMDAwJykpCn0KYGBgCgotICBUU1MgZW5yaWNobWVudCBzY29yZQpUaGUgZW5yaWNobWVudCBvZiBUbjUgaW50ZWdyYXRpb24gZXZlbnRzIGF0IHRyYW5zY3JpcHRpb25hbCBzdGFydCBzaXRlcyAoVFNTcykgY2FuIGFsc28gYmUgYW4gaW1wb3J0YW50IHF1YWxpdHkgY29udHJvbCBtZXRyaWMgdG8gYXNzZXNzIHRoZSB0YXJnZXRpbmcgb2YgVG41IGluIEFUQUMtc2VxIGV4cGVyaW1lbnRzLiBUaGUgRU5DT0RFIGNvbnNvcnRpdW0gZGVmaW5lZCBhIFRTUyBlbnJpY2htZW50IHNjb3JlIGFzIHRoZSBudW1iZXIgb2YgVG41IGludGVncmF0aW9uIHNpdGUgYXJvdW5kIHRoZSBUU1Mgbm9ybWFsaXplZCB0byB0aGUgbnVtYmVyIG9mIFRuNSBpbnRlZ3JhdGlvbiBzaXRlcyBpbiBmbGFua2luZyByZWdpb25zLiBTZWUgdGhlIEVOQ09ERSBkb2N1bWVudGF0aW9uIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBUU1MgZW5yaWNobWVudCBzY29yZSAoaHR0cHM6Ly93d3cuZW5jb2RlcHJvamVjdC5vcmcvZGF0YS1zdGFuZGFyZHMvdGVybXMvKS4gCgpgYGB7cn0KZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZS5uYW1lcykpewpzZXVyW1tpXV0gPC0gVFNTRW5yaWNobWVudChzZXVyW1tpXV0sIHZlcmJvc2U9RikKfQpgYGAKCi0gRnJhY3Rpb24gb2YgZnJhZ21lbnRzIGluIHBlYWtzClJlcHJlc2VudHMgdGhlIGZyYWN0aW9uIG9mIGFsbCBmcmFnbWVudHMgdGhhdCBmYWxsIHdpdGhpbiBBVEFDLXNlcSBwZWFrcy4gQ2VsbHMgd2l0aCBsb3cgdmFsdWVzIChpLmUuIDwxNS0yMCUpIG9mdGVuIHJlcHJlc2VudCBsb3ctcXVhbGl0eSBjZWxscyBvciB0ZWNobmljYWwgYXJ0aWZhY3RzIHRoYXQgc2hvdWxkIGJlIHJlbW92ZWQuIE5vdGUgdGhhdCB0aGlzIHZhbHVlIGNhbiBiZSBzZW5zaXRpdmUgdG8gdGhlIHNldCBvZiBwZWFrcyB1c2VkLgpgYGB7cn0KZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZS5uYW1lcykpewpzZXVyW1tpXV0gJHBjdF9yZWFkc19pbl9wZWFrcyA8LSBzZXVyW1tpXV0gJHBlYWtfcmVnaW9uX2ZyYWdtZW50cyAvIHNldXJbW2ldXSAkcGFzc2VkX2ZpbHRlcnMgKiAxMDAKfQpgYGAKCi0gVG90YWwgbnVtYmVyIG9mIGZyYWdtZW50cyBpbiBwZWFrczogInBlYWtfcmVnaW9uX2ZyYWdtZW50cyIgbWV0cmljLCBjYWxjdWxhdGVkIGJ5IENlbGxSYW5nZXIKQSBtZWFzdXJlIG9mIGNlbGx1bGFyIHNlcXVlbmNpbmcgZGVwdGggLyBjb21wbGV4aXR5LiBDZWxscyB3aXRoIHZlcnkgZmV3IHJlYWRzIG1heSBuZWVkIHRvIGJlIGV4Y2x1ZGVkIGR1ZSB0byBsb3cgc2VxdWVuY2luZyBkZXB0aC4gQ2VsbHMgd2l0aCBleHRyZW1lbHkgaGlnaCBsZXZlbHMgbWF5IHJlcHJlc2VudCBkb3VibGV0cywgbnVjbGVpIGNsdW1wcywgb3Igb3RoZXIgYXJ0ZWZhY3RzLgoKLSBSYXRpbyByZWFkcyBpbiBnZW5vbWljIGJsYWNrbGlzdCByZWdpb25zClRoZSBFTkNPREUgcHJvamVjdCBoYXMgcHJvdmlkZWQgYSBsaXN0IG9mIGJsYWNrbGlzdCByZWdpb25zLCByZXByZXNlbnRpbmcgcmVhZHMgd2hpY2ggYXJlIG9mdGVuIGFzc29jaWF0ZWQgd2l0aCBhcnRlZmFjdHVhbCBzaWduYWwuIENlbGxzIHdpdGggYSBoaWdoIHByb3BvcnRpb24gb2YgcmVhZHMgbWFwcGluZyB0byB0aGVzZSBhcmVhcyAoY29tcGFyZWQgdG8gcmVhZHMgbWFwcGluZyB0byBwZWFrcykgb2Z0ZW4gcmVwcmVzZW50IHRlY2huaWNhbCBhcnRpZmFjdHMgYW5kIHNob3VsZCBiZSByZW1vdmVkLgpgYGB7cn0Kc2V1cltbaV1dJGJsYWNrbGlzdF9yYXRpbyA8LSBGcmFjdGlvbkNvdW50c0luUmVnaW9uKAogIG9iamVjdCA9IHNldXJbW2ldXSwgCiAgYXNzYXkgPSAncGVha3MnLAogIHJlZ2lvbnMgPSBtbTEwLmJsYWNrbGlzdAopCmBgYAoKCgojIyMgRmlkbmluZyBvdXRsaWVyIG51Y2xlaSAKVGhlIGV4YWN0IFFDIHRocmVzaG9sZHMgZm9yIHJlbW92aW5nIG91dGxpZXJzIG5lZWQgdG8gYmUgYWRqdXN0ZWQgYWNjb3JkaW5nIHRvIHRoZSBkYXRhc2V0LgpIZXJlLCB3ZSB1c2UgdGhlIGlzT3V0bGllcigpIGZ1bmN0aW9uIGZyb20gdGhlIHNjdXR0bGUgcGFja2FnZS4gSXQgZGVmaW5lcyBhbiBvYnNlcnZhdGlvbiBhcyBhbiBvdXRsaWVyIGlmIGl0IGlzIG1vcmUgdGhhbiBhIHNwZWNpZmllZCBudW1iZXIgb2YgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbnMgKE1BRHMsIGRlZmF1bHQgMykgZnJvbSB0aGUgbWVkaWFuIGluIHRoZSBzcGVjaWZpZWQgZGlyZWN0aW9uLgoKYGBge3J9CiAgaj0gJ251Y2xlb3NvbWVfc2lnbmFsJwpmb3IgKGkgaW4gMTpsZW5ndGgoc2FtcGxlLm5hbWVzKSl7CiAgb3V0bGllcnM9IHNjdXR0bGU6OmlzT3V0bGllcihzZXVyW1tpXV1AbWV0YS5kYXRhWyxqXSwgbm1hZHM9NiwgdHlwZT0iaGlnaGVyIiwgbG9nPVRSVUUpCiAgYXR0cihvdXRsaWVycywgInRocmVzaG9sZHMiKSA8LSBOVUxMCiBzZXVyW1tpXV1AbWV0YS5kYXRhW3Bhc3RlMChqLCIub3V0bGllci5oaWdoZXIiKV0gPC0gb3V0bGllcnMKICBjYXQoc2FtcGxlLm5hbWVzW2ldLGosIm91dGxpZXJzIGhpZ2hlciA6IixzdW0oc2V1cltbaV1dQG1ldGEuZGF0YVtwYXN0ZTAoaiwiLm91dGxpZXIuaGlnaGVyIildKSwiXG4iKQp9Cgpmb3IgKCBqIGluIGMoJ3BjdF9yZWFkc19pbl9wZWFrcycsJ1RTUy5lbnJpY2htZW50JykpewogZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZS5uYW1lcykpewogICAgb3V0bGllcnM9IHNjYXRlcjo6aXNPdXRsaWVyKHNldXJbW2ldXUBtZXRhLmRhdGFbLGpdLCBubWFkcz02LCB0eXBlPSJsb3dlciIsIGxvZz1UUlVFKQogIGF0dHIob3V0bGllcnMsICJ0aHJlc2hvbGRzIikgPC0gTlVMTAogc2V1cltbaV1dQG1ldGEuZGF0YVtwYXN0ZTAoaiwiLm91dGxpZXIubG93ZXIiKV0gPC0gb3V0bGllcnMKICBjYXQoc2FtcGxlLm5hbWVzW2ldLGosIm91dGxpZXJzIGxvd2VyIDoiLHN1bShzZXVyW1tpXV1AbWV0YS5kYXRhW3Bhc3RlMChqLCIub3V0bGllci5sb3dlciIpXSksIlxuIikKICB9Cn0KCmo9J3BlYWtfcmVnaW9uX2ZyYWdtZW50cycKZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZS5uYW1lcykpewogICAgb3V0bGllcnM9IHNjYXRlcjo6aXNPdXRsaWVyKHNldXJbW2ldXUBtZXRhLmRhdGFbLGpdLCBubWFkcz0yLCB0eXBlPSJsb3dlciIsIGxvZz1UUlVFKQogIGF0dHIob3V0bGllcnMsICJ0aHJlc2hvbGRzIikgPC0gTlVMTAogc2V1cltbaV1dQG1ldGEuZGF0YVtwYXN0ZTAoaiwiLm91dGxpZXIubG93ZXIiKV0gPC0gb3V0bGllcnMKICBjYXQoc2FtcGxlLm5hbWVzW2ldLGosIm91dGxpZXJzIGxvd2VyIDoiLHN1bShzZXVyW1tpXV1AbWV0YS5kYXRhW3Bhc3RlMChqLCIub3V0bGllci5sb3dlciIpXSksIlxuIikKfQoKaj0iYmxhY2tsaXN0X3JhdGlvIgpmb3IgKGkgaW4gMTpsZW5ndGgoc2FtcGxlLm5hbWVzKSl7CiAgb3V0bGllcnM9IHNjYXRlcjo6aXNPdXRsaWVyKHNldXJbW2ldXUBtZXRhLmRhdGFbLGpdLCBubWFkcz0zLCB0eXBlPSJoaWdoZXIiLCBsb2c9VFJVRSkKICBhdHRyKG91dGxpZXJzLCAidGhyZXNob2xkcyIpIDwtIE5VTEwKIHNldXJbW2ldXUBtZXRhLmRhdGFbcGFzdGUwKGosIi5vdXRsaWVyLmhpZ2hlciIpXSA8LSBvdXRsaWVycwogIGNhdChzYW1wbGUubmFtZXNbaV0saiwib3V0bGllcnMgaGlnaGVyIDoiLHN1bShzZXVyW1tpXV1AbWV0YS5kYXRhW3Bhc3RlMChqLCIub3V0bGllci5oaWdoZXIiKV0pLCJcbiIpCn0KYGBgCgoKCmBgYHtyICwgZmlnLmhlaWdodCA9IDMuNSwgZmlnLndpZHRoID0gMTB9CmZvciAoIGogaW4gYygncGVha19yZWdpb25fZnJhZ21lbnRzJywncGN0X3JlYWRzX2luX3BlYWtzJywgJ251Y2xlb3NvbWVfc2lnbmFsJywKICAgICAgICAgICAgICAgJ1RTUy5lbnJpY2htZW50JywgImJsYWNrbGlzdF9yYXRpbyIpKXsKICBmb3IgKGkgaW4gMTpsZW5ndGgoc2FtcGxlLm5hbWVzKSl7CiAgICAgaGlzdChzZXVyW1tpXV1AbWV0YS5kYXRhWyxqXSwKICAgICAgICBicmVha3MgPSAxMDAseGxhYj1qLAogICAgICAgIG1haW49cGFzdGUwKGosIjogIixzYW1wbGUubmFtZXNbaV0pKQogICAgaWYgKHBhc3RlMChqLCIub3V0bGllci5oaWdoZXIiKSAlaW4lIGNvbG5hbWVzKHNldXJbW2ldXUBtZXRhLmRhdGEpICl7CiAgICAgIGlmKHN1bShzZXVyW1tpXV1AbWV0YS5kYXRhW3Bhc3RlMChqLCIub3V0bGllci5oaWdoZXIiKV0pICE9MCApCiAgICAgICBhYmxpbmUodiA9IG1pbihzZXVyW1tpXV1AbWV0YS5kYXRhWyxqXVt1bmxpc3Qoc2V1cltbaV1dQG1ldGEuZGF0YVtwYXN0ZTAoaiwiLm91dGxpZXIuaGlnaGVyIildKV0pLCBjb2wgPSAicmVkIikKICAgIH0KICAgaWYgKHBhc3RlMChqLCIub3V0bGllci5sb3dlciIpICVpbiUgY29sbmFtZXMoc2V1cltbaV1dQG1ldGEuZGF0YSkgKXsKICAgIGlmKHN1bShzZXVyW1tpXV1AbWV0YS5kYXRhW3Bhc3RlMChqLCIub3V0bGllci5sb3dlciIpXSkgIT0wICkKICAgYWJsaW5lKHYgPSBtYXgoc2V1cltbaV1dQG1ldGEuZGF0YVssal1bdW5saXN0KHNldXJbW2ldXUBtZXRhLmRhdGFbcGFzdGUwKGosIi5vdXRsaWVyLmxvd2VyIildKV0pLCBjb2wgPSAicmVkIikKICAgfQogIH0KfQpgYGAKCgoKV2UgcmVtb3ZlIGNlbGxzIHRoYXQgYXJlIG91dGxpZXJzIGZvciB0aGVzZSBRQyBtZXRyaWNzLgoKYGBge3J9CmZvciAoaSBpbiAxOmxlbmd0aChzYW1wbGUubmFtZXMpKXsKc2V1cltbaV1dICA8LSBzZXVyW1tpXV0gWywhKHNldXJbW2ldXSRwZWFrX3JlZ2lvbl9mcmFnbWVudHMub3V0bGllci5sb3dlciB8IAogICAgc2V1cltbaV1dJHBjdF9yZWFkc19pbl9wZWFrcy5vdXRsaWVyLmxvd2VyIHwgCiAgICBzZXVyW1tpXV0kbnVjbGVvc29tZV9zaWduYWwub3V0bGllci5oaWdoZXIgIHwgCiAgICBzZXVyW1tpXV0kVFNTLmVucmljaG1lbnQub3V0bGllci5sb3dlciB8CiAgIHNldXJbW2ldXSRibGFja2xpc3RfcmF0aW8ub3V0bGllci5oaWdoZXIgICApXQoKcHJpbnQoc2FtcGxlLm5hbWVzW2ldKQpwcmludChzZXVyW1tpXV0gKQp9CmBgYAoKCmBgYHtyfQpzZXVyPC1tZXJnZShzZXVyW1sxXV0sc2V1clsyOmxlbmd0aChzZXVyKV0pCmBgYAoKCiMjIyBOb3JtYWxpemF0aW9uIGFuZCBsaW5lYXIgZGltZW5zaW9uYWwgcmVkdWN0aW9uCgogICAgTm9ybWFsaXphdGlvbjogU2lnbmFjIHBlcmZvcm1zIHRlcm0gZnJlcXVlbmN5LWludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5IChURi1JREYpIG5vcm1hbGl6YXRpb24uIFRoaXMgaXMgYSB0d28tc3RlcCBub3JtYWxpemF0aW9uIHByb2NlZHVyZSwgdGhhdCBib3RoIG5vcm1hbGl6ZXMgYWNyb3NzIGNlbGxzIHRvIGNvcnJlY3QgZm9yIGRpZmZlcmVuY2VzIGluIGNlbGx1bGFyIHNlcXVlbmNpbmcgZGVwdGgsIGFuZCBhY3Jvc3MgcGVha3MgdG8gZ2l2ZSBoaWdoZXIgdmFsdWVzIHRvIG1vcmUgcmFyZSBwZWFrcy4KCiAgICBGZWF0dXJlIHNlbGVjdGlvbjogVGhlIGxvdyBkeW5hbWljIHJhbmdlIG9mIHNjQVRBQy1zZXEgZGF0YSBtYWtlcyBpdCBjaGFsbGVuZ2luZyB0byBwZXJmb3JtIHZhcmlhYmxlIGZlYXR1cmUgc2VsZWN0aW9uLCBhcyB3ZSBkbyBmb3Igc2NSTkEtc2VxLiBJbnN0ZWFkLCB3ZSBjYW4gY2hvb3NlIHRvIHVzZSBvbmx5IHRoZSB0b3AgbiUgb2YgZmVhdHVyZXMgKHBlYWtzKSBmb3IgZGltZW5zaW9uYWwgcmVkdWN0aW9uLCBvciByZW1vdmUgZmVhdHVyZXMgcHJlc2VudCBpbiBsZXNzIHRoYW4gbiBjZWxscyB3aXRoIHRoZSBGaW5kVG9wRmVhdHVyZXMoKSBmdW5jdGlvbi4gSGVyZSB3ZSB3aWxsIHVzZSBhbGwgZmVhdHVyZXMsIHRob3VnaCB3ZSBoYXZlIHNlZW4gdmVyeSBzaW1pbGFyIHJlc3VsdHMgd2hlbiB1c2luZyBvbmx5IGEgc3Vic2V0IG9mIGZlYXR1cmVzICh0cnkgc2V0dGluZyBtaW4uY3V0b2ZmIHRvIOKAmHE3NeKAmSB0byB1c2UgdGhlIHRvcCAyNSUgYWxsIHBlYWtzKSwgd2l0aCBmYXN0ZXIgcnVudGltZXMuIEZlYXR1cmVzIHVzZWQgZm9yIGRpbWVuc2lvbmFsIHJlZHVjdGlvbiBhcmUgYXV0b21hdGljYWxseSBzZXQgYXMgVmFyaWFibGVGZWF0dXJlcygpIGZvciB0aGUgU2V1cmF0IG9iamVjdCBieSB0aGlzIGZ1bmN0aW9uLgoKICAgIERpbWVuc2lvbiByZWR1Y3Rpb246IFdlIG5leHQgcnVuIHNpbmd1bGFyIHZhbHVlIGRlY29tcG9zaXRpb24gKFNWRCkgb24gdGhlIFRELUlERiBtYXRyaXgsIHVzaW5nIHRoZSBmZWF0dXJlcyAocGVha3MpIHNlbGVjdGVkIGFib3ZlLiBUaGlzIHJldHVybnMgYSByZWR1Y2VkIGRpbWVuc2lvbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgb2JqZWN0IChmb3IgdXNlcnMgd2hvIGFyZSBtb3JlIGZhbWlsaWFyIHdpdGggc2NSTkEtc2VxLCB5b3UgY2FuIHRoaW5rIG9mIHRoaXMgYXMgYW5hbG9nb3VzIHRvIHRoZSBvdXRwdXQgb2YgUENBKS4KClRoZSBjb21iaW5lZCBzdGVwcyBvZiBURi1JREYgZm9sbG93ZWQgYnkgU1ZEIGFyZSBrbm93biBhcyBsYXRlbnQgc2VtYW50aWMgaW5kZXhpbmcgKExTSSksIGFuZCB3ZXJlIGZpcnN0IGludHJvZHVjZWQgZm9yIHRoZSBhbmFseXNpcyBvZiBzY0FUQUMtc2VxIGRhdGEgYnkgQ3VzYW5vdmljaCBldCBhbC4gMjAxNS4KYGBge3J9CnNldXIgPC0gUnVuVEZJREYoc2V1cix2ZXJib3NlID1GKQpzZXVyIDwtIEZpbmRUb3BGZWF0dXJlcyhzZXVyLCBtaW4uY3V0b2ZmID0gJ3EwJyx2ZXJib3NlID1GKQpzZXVyIDwtIFJ1blNWRChvYmplY3QgPSBzZXVyLHZlcmJvc2UgPUYpCmBgYApUaGUgZmlyc3QgTFNJIGNvbXBvbmVudCBvZnRlbiBjYXB0dXJlcyBzZXF1ZW5jaW5nIGRlcHRoICh0ZWNobmljYWwgdmFyaWF0aW9uKSByYXRoZXIgdGhhbiBiaW9sb2dpY2FsIHZhcmlhdGlvbi4gSWYgdGhpcyBpcyB0aGUgY2FzZSwgdGhlIGNvbXBvbmVudCBzaG91bGQgYmUgcmVtb3ZlZCBmcm9tIGRvd25zdHJlYW0gYW5hbHlzaXMuIFdlIGNhbiBhc3Nlc3MgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gZWFjaCBMU0kgY29tcG9uZW50IGFuZCBzZXF1ZW5jaW5nIGRlcHRoIHVzaW5nIHRoZSBEZXB0aENvcigpIGZ1bmN0aW9uOgoKCmBgYHtyICwgZmlnLmhlaWdodCA9IDMuNSwgZmlnLndpZHRoID0gNX0KRGVwdGhDb3Ioc2V1cikKYGBgCmBgYHtyICwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDZ9CkVsYm93UGxvdChvYmplY3QgPSBzZXVyLG5kaW1zID01MCwgcmVkdWN0aW9uID0gImxzaSIpCmBgYAoKCk5vbi1saW5lYXIgZGltZW5zaW9uIHJlZHVjdGlvbiBhbmQgY2x1c3RlcmluZwoKTm93IHRoYXQgdGhlIGNlbGxzIGFyZSBlbWJlZGRlZCBpbiBhIGxvdy1kaW1lbnNpb25hbCBzcGFjZSB3ZSBjYW4gdXNlIG1ldGhvZHMgY29tbW9ubHkgYXBwbGllZCBmb3IgdGhlIGFuYWx5c2lzIG9mIHNjUk5BLXNlcSBkYXRhIHRvIHBlcmZvcm0gZ3JhcGgtYmFzZWQgY2x1c3RlcmluZyBhbmQgbm9uLWxpbmVhciBkaW1lbnNpb24gcmVkdWN0aW9uIGZvciB2aXN1YWxpemF0aW9uLiBUaGUgZnVuY3Rpb25zIFJ1blVNQVAoKSwgRmluZE5laWdoYm9ycygpLCBhbmQgRmluZENsdXN0ZXJzKCkgYWxsIGNvbWUgZnJvbSB0aGUgU2V1cmF0IHBhY2thZ2UuCgpgYGB7cn0KIyMjIExTSSBjb21wb25lbnRzIHNlbGVjdGlvbiBmb3IgZG93bnN0cmVhbSBhbmFseXNpcwpkaW1zLnVzZTwtMzAKYGBgCgoKYGBge3J9CnNldXIgPC0gUnVuVU1BUChvYmplY3QgPSBzZXVyLCByZWR1Y3Rpb24gPSAnbHNpJywgZGltcyA9IDI6ZGltcy51c2UsdmVyYm9zZSA9RikKc2V1ciA8LSBGaW5kTmVpZ2hib3JzKHNldXIsICByZWR1Y3Rpb24gPSAnbHNpJyxkaW1zID0gMjpkaW1zLnVzZSwgdmVyYm9zZT1GLCBncmFwaC5uYW1lPXBhc3RlMCgiQVRBQ19zbm5fTFNJIixkaW1zLnVzZSkpCmZvciAoIGkgaW4gc2VxKDAsMiwgMC4yNSkpCiAgc2V1ciA8LSBGaW5kQ2x1c3RlcnMoc2V1ciwgcmVzb2x1dGlvbiA9IGksIGFsZ29yaXRobSA9IDMsIGdyYXBoLm5hbWU9cGFzdGUwKCJBVEFDX3Nubl9MU0kiLGRpbXMudXNlKSwgdmVyYm9zZT1GKSAKYGBgCgoKYGBge3IgLCBmaWcuaGVpZ2h0ID0gOCwgZmlnLndpZHRoID0gMTB9CmNsdXN0cmVlOjpjbHVzdHJlZShzZXVyLCBwcmVmaXggPSBwYXN0ZTAoIkFUQUNfc25uX0xTSSIsZGltcy51c2UsIl9yZXMuIikpKwogIGdndGl0bGUocGFzdGUoIkxTSSA9IixkaW1zLnVzZSkpCmBgYAoKYGBge3IgLCBmaWcuaGVpZ2h0ID0gOCwgZmlnLndpZHRoID0gMTJ9CnBsb3Q8LWxpc3QoKQpmb3IgKCByZXMgaW4gYygwLjI1LDAuNSwwLjc1LDEpKQogIHBsb3RbW2FzLmNoYXJhY3RlcihyZXMpXV08LURpbVBsb3Qoc2V1cixsYWJlbD1ULHJlcGVsPVQsIGdyb3VwLmJ5ID0gcGFzdGUwKCJBVEFDX3Nubl9MU0kiLGRpbXMudXNlLCJfcmVzLiIscmVzKSkrCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2d0aXRsZShwYXN0ZSgiTFNJID0iLGRpbXMudXNlLCJyZXM9IixyZXMpKQpwbG90X2dyaWQocGxvdGxpc3Q9cGxvdCkKYGBgCgoKYGBge3IgLCBmaWcuaGVpZ2h0ID0gNCwgZmlnLndpZHRoID0gNX0KRGltUGxvdChzZXVyLHJlcGVsID1ULGxhYmVsPVQsIGdyb3VwLmJ5ID0gIm9yaWdpbiIpIApgYGAKCgojIyMgQ3JlYXRlIGEgZ2VuZSBhY3Rpdml0eSBtYXRyaXgKV2UgY2FuIHRyeSB0byBxdWFudGlmeSB0aGUgYWN0aXZpdHkgb2YgZWFjaCBnZW5lIGluIHRoZSBnZW5vbWUgYnkgYXNzZXNzaW5nIHRoZSBjaHJvbWF0aW4gYWNjZXNzaWJpbGl0eSBhc3NvY2lhdGVkIHdpdGggdGhlIGdlbmUsIGFuZCBjcmVhdGUgYSBuZXcgZ2VuZSBhY3Rpdml0eSBhc3NheSBkZXJpdmVkIGZyb20gdGhlIHNjQVRBQy1zZXEgZGF0YS4gSGVyZSB3ZSB3aWxsIHVzZSBhIHNpbXBsZSBhcHByb2FjaCBvZiBzdW1taW5nIHRoZSBmcmFnbWVudHMgaW50ZXJzZWN0aW5nIHRoZSBnZW5lIGJvZHkgYW5kIHByb21vdGVyIHJlZ2lvbgoKVG8gY3JlYXRlIGEgZ2VuZSBhY3Rpdml0eSBtYXRyaXgsIHdlIGV4dHJhY3QgZ2VuZSBjb29yZGluYXRlcyBhbmQgZXh0ZW5kIHRoZW0gdG8gaW5jbHVkZSB0aGUgMiBrYiB1cHN0cmVhbSByZWdpb24gKGFzIHByb21vdGVyIGFjY2Vzc2liaWxpdHkgaXMgb2Z0ZW4gY29ycmVsYXRlZCB3aXRoIGdlbmUgZXhwcmVzc2lvbikuIFdlIHRoZW4gY291bnQgdGhlIG51bWJlciBvZiBmcmFnbWVudHMgZm9yIGVhY2ggY2VsbCB0aGF0IG1hcCB0byBlYWNoIG9mIHRoZXNlIHJlZ2lvbnMsIHVzaW5nIHRoZSB1c2luZyB0aGUgRmVhdHVyZU1hdHJpeCgpIGZ1bmN0aW9uLiBUaGVzZSBzdGVwcyBhcmUgYXV0b21hdGljYWxseSBwZXJmb3JtZWQgYnkgdGhlIEdlbmVBY3Rpdml0eSgpIGZ1bmN0aW9uOgoKYGBge3J9CmdlbmUuYWN0aXZpdGllcyA8LSBHZW5lQWN0aXZpdHkoc2V1cix2ZXJib3NlID1GKQoKc2V1cltbJ1JOQSddXSA8LSBDcmVhdGVBc3NheU9iamVjdChjb3VudHMgPSBnZW5lLmFjdGl2aXRpZXMpCiAjIGFkZCB0aGUgZ2VuZSBhY3Rpdml0eSBtYXRyaXggdG8gdGhlIFNldXJhdCBvYmplY3QgYXMgYSBuZXcgYXNzYXkgYW5kIG5vcm1hbGl6ZSBpdCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKc2V1ciA8LSBOb3JtYWxpemVEYXRhKAogIG9iamVjdCA9IHNldXIsCiAgYXNzYXkgPSAnUk5BJywKICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICdMb2dOb3JtYWxpemUnLAogIHNjYWxlLmZhY3RvciA9IG1lZGlhbihzZXVyJG5Db3VudF9STkEpLAogIHZlcmJvc2UgPUYKKQpzZXVyIDwtIFNjYWxlRGF0YShzZXVyLCBhc3NheSA9ICdSTkEnLHZlcmJvc2UgPUYpCmBgYAoKCmBgYHtyfQpEZWZhdWx0QXNzYXkoc2V1cikgPC0gJ1JOQScKcmVzPTAuNQpJZGVudHMoc2V1cik9ICBwYXN0ZTAoIkFUQUNfc25uX0xTSSIsZGltcy51c2UsIl9yZXMuIixyZXMpCklkZW50cyhzZXVyKT0gIGZhY3RvcihJZGVudHMoc2V1ciksbGV2ZWxzID0gMDoobGVuZ3RoKHVuaXF1ZShJZGVudHMoc2V1cikpKS0xKSkKREVnZW5lcz1saXN0KCkKZm9yIChpIGluIGxldmVscyhJZGVudHMoc2V1cikpKXsKIERFZ2VuZXNbW2ldXTwtRmluZE1hcmtlcnMoc2V1ciwgaWRlbnQuMSA9IGksbWluLmNlbGxzLmdyb3VwPTIscHNldWRvY291bnQudXNlID0gMC4wMSwgbWF4LmNlbGxzLnBlci5pZGVudCA9IDEwMDApCiBERWdlbmVzW1tpXV0kY2x1c3Rlcj1pCiBERWdlbmVzW1tpXV0kcHNldWRvY291bnQ9MC4wMQogREVnZW5lc1tbaV1dJG1heC5jZWxscy5wZXIuaWRlbnQ9MTAwMAogREVnZW5lc1tbaV1dJGdlbmU9cm93Lm5hbWVzKERFZ2VuZXNbW2ldXSkKfQpgYGAKCgpgYGB7cn0KZmVhdHVyZXMudXNlPXVubGlzdChsYXBwbHkoREVnZW5lcywgZnVuY3Rpb24oeCkgeyBoZWFkKHhbeCRhdmdfbG9nMkZDPjAsXSRnZW5lLDcpfSkpCmxlbmd0aChmZWF0dXJlcy51c2UpCm5hbWVzKGZlYXR1cmVzLnVzZSk9TlVMTApmZWF0dXJlcy51c2U9ZmVhdHVyZXMudXNlWyFkdXBsaWNhdGVkKGZlYXR1cmVzLnVzZSldCmBgYAoKYGBge3IgLCBmaWcuaGVpZ2h0ID0gNCwgZmlnLndpZHRoID0gNX0KRGltUGxvdChzZXVyLHJlcGVsID1ULGxhYmVsPVQpIApgYGAKCmBgYHtyICwgZmlnLmhlaWdodCA9NCwgZmlnLndpZHRoID0xNH0KRG90UGxvdChzZXVyLCBmZWF0dXJlcyA9IGZlYXR1cmVzLnVzZSkrUm90YXRlZEF4aXMoKStOb0xlZ2VuZCgpCmBgYAoKCmBgYHtyfQpJZGVudHMoc2V1cikgPC0gcGx5cjo6bWFwdmFsdWVzKHggPSBJZGVudHMoc2V1ciksIGZyb20gPSAwOjEwLCAKICAgIHRvID1jKCJGTCAyIiwgIkJNIDMiLCJCTSAxIiwiRkwgMSIsIkJNIDQiLCJCTSAyIiwKICAgICAgICAgICJGTCAzIiwgIkZMIDUiLCAiRXJ5dGhyb2lkIiwiRkwgNCIsIkx5bXBob2lkIikpCmBgYAoKYGBge3J9Cm5ldy5vcmRlcj1jICggICJGTCAxIiwgIkZMIDIiICwgIkZMIDMiICwgICJGTCA0IiwiRkwgNSIgLCAgIkJNIDEiICAsICJCTSAyIiwgIkJNIDMiICAgLCAgIkJNIDQiICwgICJFcnl0aHJvaWQiICwgIkx5bXBob2lkIiAgICAgICApCm5ldy5vcmRlclshbmV3Lm9yZGVyICVpbiUgbGV2ZWxzKCBJZGVudHMoc2V1cikpXQpsZXZlbHMoIElkZW50cyhzZXVyKSlbIWxldmVscyggSWRlbnRzKHNldXIpKSAlaW4lbmV3Lm9yZGVyXQpgYGAKCgoKYGBge3J9CklkZW50cyhzZXVyKT1mYWN0b3IoIElkZW50cyhzZXVyKSwgbGV2ZWxzPW5ldy5vcmRlcikKc2V1ciRhbm5vdD1JZGVudHMoc2V1cikKYGBgCmBgYHtyICwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDh9CkRpbVBsb3Qoc2V1cixyZXBlbCA9VCxsYWJlbD1UKSAKYGBgCgoKYGBge3IgLCBmaWcuaGVpZ2h0ID0gNCwgZmlnLndpZHRoID0xNH0KRG90UGxvdChzZXVyLCBmZWF0dXJlcz1jKCJQdHByYyIsIkZsdDMiLCJUc2h6MiIsIklnZjJicDMiICwgIkdsaTIiLCJDZDM0IiwiUHJ0bjMiLCJLaXQiLCJNcG8iLCJMcG8iLCJFbGFuZSIgLCJTb3JjczIiLCJQYXg1IiwiUm9yMSIsIlRtdGMxIiwiQzFxYSIsIkMxcWIiLCJNczRhNyIsIkFwb2UiLCJGY3JscyIsIkNkazgiLCJMYXJzMiIsIlBsZWMiLCJTMTAwYTEwIiwiRjEzYTEiLCJMeTZjMiIsIkNyaXAxIiAsIkNjcjIiICwiTHl6MiIsICJUZ2ZiaSIsIk1yYzEiLCJVc3QiLCJIMi1BYSIsIkNkNzQiLCJBdGYzIiAsIktsZjEzIiAgICwgIkdyazUiICAsIkRvY2s1IiAgLCAiQ3lwMmFiMSIgLCAgIkFjZSIsIkVhcjIiLCJBbmsxIiwiR3lwYSIsIkFxcDEiLCJDYXIyIiwiSGJiLWJ0IiAsIkNjcjciLCJDZDc5YSIsIk1zNGExIiwiRWJmMSIsIkRudHQiLCJNa2k2NyIsIlN0bW4xIiwibnVjbGVvc29tZV9zaWduYWwiLCJudWNsZW9zb21lX3BlcmNlbnRpbGUiLCJUU1MuZW5yaWNobWVudCIsIlRTUy5wZXJjZW50aWxlIiwicGN0X3JlYWRzX2luX3BlYWtzIiwiYmxhY2tsaXN0X3JhdGlvIikpK1JvdGF0ZWRBeGlzKCkrdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKYGBge3IgLCBmaWcuaGVpZ2h0ID0gMywgZmlnLndpZHRoID0gMTB9Cm1ldGEuZGF0YTwtc2V1ckBtZXRhLmRhdGEgJT4lCiAgICBkcGx5cjo6Z3JvdXBfYnkoYW5ub3Qsb3JpZ2luKSAlPiUKICAgIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9ZHBseXI6Om4oKSklPiUgc3VwcHJlc3NNZXNzYWdlcygpJT4lCiAgICBkcGx5cjo6Z3JvdXBfYnkoYW5ub3QpICU+JQogICAgZHBseXI6Om11dGF0ZShwZXJjLnBlci5ncm91cCA9IChjb3VudCAvIHN1bShjb3VudCkpKjEwMCkgIApnZ3Bsb3QobWV0YS5kYXRhLCBhZXMoeD1hbm5vdCwgeT1wZXJjLnBlci5ncm91cCwgZmlsbD1vcmlnaW4pKSsKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIpKSsKICAgIHlsYWIoIiUgY2VsbHMgcGVyIGNsdXN0ZXIiKStSb3RhdGVkQXhpcygpCmBgYAoKYGBge3J9CnNhdmVSRFMoc2V1cixwYXN0ZTAoIkludGVncmF0ZWQuQk0uYW5kLkZMX21vbm9jeXRlc19zbkFUQUNzZXFfSkJBMS0yLnNldXJhdC5yZHMiKSkKYGBgCgoKCgpgYGB7cix3YXJuaW5nPUZBTFNFfQpzZXNzaW9uSW5mbygpCmBgYAo=